package org.atlantis.grosbeak.depot;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.atlantis.grosbeak.castor.Castors;
import org.atlantis.grosbeak.castor.FailToCastException;
import org.atlantis.grosbeak.depot.desc.EventDesc;
import org.atlantis.grosbeak.depot.desc.FieldDesc;
import org.atlantis.grosbeak.depot.desc.ObjectDesc;
import org.atlantis.grosbeak.depot.desc.ValueDesc;
import org.atlantis.grosbeak.lang.Exceptions;
import org.atlantis.grosbeak.lang.Mirror;
import org.atlantis.grosbeak.lang.Pair;
import org.atlantis.grosbeak.lang.Strings;
import org.atlantis.grosbeak.lang.each.Each;
import org.atlantis.grosbeak.lang.each.Eachs;

/**
 * Depot容器的工具类。
 * 
 * @author <a href="mailto:seenoevil.cn@gmail.com">SeeNoEvil</a>
 * 
 */
@SuppressWarnings("unchecked")
public abstract class DepotUtils {

	// JSON配置中属于Depot系统字段的正则表达式。
	private final static String DEPOTFIELDS = "^(type|scope|singleton|fields|args|event)$";

	/**
	 * 判断一个Map中的key是不是都是系统属性。<br>
	 * 注 - 系统属性如下：
	 * <ul>
	 * <li>type：对象的类型。</li>
	 * <li>scope：对象所存在的环境。</li>
	 * <li>singleton：对象是否单态。</li>
	 * <li>fields：对象的字段。</li>
	 * <li>args：对象构造方法的参数。</li>
	 * <li>event：对象的事件。</li>
	 * </ul>
	 * 
	 * @param map
	 *            需要判断的Map。
	 * @return 如果Map中所有的key都是不是系统属性，则返回true；否则，只要有一个是系统属性，则返回false。
	 */
	public static boolean isDepotField(Map<String, ?> map) {
		for (Entry<String, ?> entry : map.entrySet()) {
			if (!entry.getKey().matches(DEPOTFIELDS))
				return false;
		}
		return true;
	}

	/**
	 * 从配置文件中获取对象的描述。
	 * 
	 * @param map
	 *            包含配置文件信息的Map。
	 * @see org.atlantis.grosbeak.depot.desc.ObjectDesc
	 * @return 对象的描述。
	 * @throws DepotException
	 */
	public static ObjectDesc mapToObjectDesc(Map<String, Object> map)
			throws DepotException {
		final ObjectDesc od = new ObjectDesc();
		if (!isDepotField(map)) {
			for (Entry<String, Object> entry : map.entrySet()) {
				FieldDesc fd = new FieldDesc();
				fd.setName(entry.getKey());
				fd.setValue(objectToValueDesc(entry.getValue()));
				od.addField(fd);
			}
		} else {
			Object v = map.get("type");

			// 获得对象类型描述
			try {
				String typeName = (String) v;
				if (!Strings.isBlank(typeName)) {
					od.setType(Class.forName(typeName));
				}
			} catch (Exception e) {
				throw Exceptions.makeThrow(DepotException.class,
						"Wrong type: '%s'", v);
			}

			// 获取对象单例情况
			try {
				v = map.get("singleton");
				if (null != v)
					od.setSingleton(Castors.me().castTo(v, boolean.class));
			} catch (FailToCastException e) {
				throw Exceptions.makeThrow(DepotException.class,
						"Singleton must be boolean type. but here is '%s'", v);
			}

			// 获取对象存在的环境
			v = map.get("scope");
			if (null != v)
				od.setScope(v.toString());

			// 获取对象的事件
			try {
				v = map.get("event");
				if (null != v) {
					EventDesc ed = mapToObject((Map<?, ?>) v, EventDesc.class);
					od.setEvent(ed);
				}
			} catch (RuntimeException e) {
				throw Exceptions.makeThrow(DepotException.class,
						"Wrong event: '%s'.", v);
			}

			// 获取对象的构造方法参数
			try {
				v = map.get("args");
				if (null != v) {
					Eachs.each(v, new Each<Object>() {
						public void go(int i, Object t, int length) {
							od.addArg(objectToValueDesc(t));
						}
					});
				}
			} catch (Exception e) {
				throw Exceptions.makeThrow(DepotException.class,
						"Wrong args: '%s'", v);
			}

			// 获取对象的字段
			try {
				v = map.get("fields");
				if (null != v) {
					Map<String, Object> fields = (Map<String, Object>) v;
					for (Entry<String, Object> entry : fields.entrySet()) {
						FieldDesc fd = new FieldDesc();
						fd.setName(entry.getKey());
						fd.setValue(objectToValueDesc(entry.getValue()));
						od.addField(fd);
					}
				}
			} catch (Exception e) {
				throw Exceptions.makeThrow(DepotException.class,
						"Wrong args: '%s'", v);
			}
		}
		return od;
	}

	/**
	 * 把一个配置文件中的值转换成对象中值的描述。
	 * 
	 * @param obj
	 *            配置文件中的值。
	 * @see org.atlantis.grosbeak.depot.desc.ValueDesc
	 * @return 对象中值的描述。
	 */
	public static ValueDesc objectToValueDesc(Object obj) {
		ValueDesc vd = new ValueDesc();

		// Object为null的情况
		if (null == obj) {
			vd.setType("null");
			return vd;
		}

		// Object为ValueDesc的情况
		else if (obj instanceof ValueDesc) {
			return (ValueDesc) obj;
		}

		// Object为Map的情况
		else if (obj instanceof Map<?, ?>) {
			Map<String, Object> map = (Map<String, Object>) obj;
			if (map.size() == 1) {
				Entry<String, ?> entry = map.entrySet().iterator().next();
				String key = entry.getKey();
				if (key.matches("^(refer|env|file)$")) {
					vd.setType(key);
					vd.setValue(entry.getValue());
					return vd;
				}
			}
			// 匿名内部类情况
			if (isDepotField(map)) {
				vd.setType("inner");
				try {
					vd.setValue(mapToObjectDesc(map));
				} catch (DepotException e) {
					throw e;
				}
				return vd;
			}
			// 普通情况
			Map<String, ValueDesc> m = new HashMap<String, ValueDesc>();
			for (Entry<String, Object> entry : map.entrySet()) {
				ValueDesc v = objectToValueDesc(entry.getValue());
				m.put(entry.getKey(), v);
			}
			vd.setType("normal");
			vd.setValue(m);
			return vd;
		}

		// 数组的情况
		else if (obj.getClass().isArray()) {
			Object[] arr = (Object[]) obj;
			ValueDesc[] vs = new ValueDesc[arr.length];
			for (int i = 0; i < vs.length; i++) {
				vs[i] = objectToValueDesc(vs[i]);
			}
			vd.setType("normal");
			vd.setValue(vs);
		}

		// Collection集合的情况
		else if (obj instanceof Collection<?>) {
			try {
				Collection<ValueDesc> vs = (Collection<ValueDesc>) obj
						.getClass().newInstance();
				Iterator<?> iter = ((Collection<?>) obj).iterator();
				while (iter.hasNext()) {
					Object o = iter.next();
					ValueDesc v = objectToValueDesc(o);
					vs.add(v);
				}
				vd.setType("normal");
				vd.setValue(vs);
				return vd;
			} catch (Exception e) {
				throw Exceptions.wrapThrow(DepotException.class, e);
			}
		}

		// 普通情况
		vd.setType("normal");
		vd.setValue(obj);
		return vd;
	}

	// 把一个Map转换成一个指定类型的对象。
	static <T> T mapToObject(Map<?, ?> src, Class<T> type)
			throws RuntimeException {
		if (null == type)
			throw Exceptions.makeThrow("type can not be null.");
		if (Map.class == type)
			return (T) src;
		if (Map.class.isAssignableFrom(type)) {
			Map map;
			try {
				map = (Map) type.newInstance();
				map.putAll(src);
				return (T) map;
			} catch (Exception e) {
				throw Exceptions.makeThrow("can not find the type '%s'.", type
						.getName());
			}
		}
		Mirror<T> mirror = Mirror.me(type);
		T re = mirror.fruit();
		for (Field field : mirror.getFields()) {
			Object v = src.get(field.getName());
			Object vac = Castors.me().castTo(v, field.getType());
			mirror.setValue(re, field, vac);
		}
		return re;
	}

	/**
	 * 把配置文件中的属性名转换为一个Pair键 - 值对。<br>
	 * 如：在引用其他对象时，配置应该是：refer:'fox:org.atlantis.grosbeak.depot.beans.Animal'，
	 * 那么此方法的作用就是把refer:'fox:org.atlantis.grosbeak.depot.beans.Animal'分解成：
	 * <ul>
	 * <li>refer</li>
	 * <li>fox:org.atlantis.grosbeak.depot.beans.Animal</li>
	 * </ul>
	 * 
	 * @param name
	 *            配置文件中的属性名。
	 * @return Pair对象。
	 */
	public static Pair<Class<?>> parseName(String name) {
		String _name = null;
		Class<?> type = null;
		int pos = name.indexOf(':');
		if (pos < 0) {
			_name = Strings.trim(name);
		} else {
			_name = Strings.trim(name.substring(0, pos));
			try {
				String typeName = Strings.trim(name.substring(pos + 1));
				type = Class.forName(typeName);
			} catch (ClassNotFoundException e) {
				throw Exceptions.wrapThrow(e);
			}
		}
		return new Pair<Class<?>>(_name, type);
	}

	/**
	 * 合并子类和父类。 如果子类中缺少相关属性，则从父类中查找。<br>
	 * 合并方式如下：
	 * <ul>
	 * <li>类型合并：如果子类没有申明类型，那么就把子类的类型定义成父类的类型。</li>
	 * <li>事件合并：给子类添加 - 父类中有，而子类中没有的事件。</li>
	 * <li>合并环境：如果子类没有定义环境（scope），那么就把父类的环境赋给子类。</li>
	 * <li>合并构造方法参数：如果子类没有构造方法参数，那么就把父类的构造方法参数赋给子类。</li>
	 * <li>合并字段：给子类添加 - 父类中有，而子类中没有的字段。</li>
	 * 
	 * @param self
	 *            子类。
	 * @param parent
	 *            父类。
	 * @return 合并后的子类。
	 */
	public static ObjectDesc merge(ObjectDesc self, ObjectDesc parent) {
		// 合并类型
		if (self.getType() == null)
			self.setType(parent.getType());

		// 合并事件
		if (self.getEvent() == null) {
			self.setEvent(parent.getEvent());
		} else {
			EventDesc ped = parent.getEvent();
			EventDesc sed = self.getEvent();
			if (Strings.isBlank(sed.getCreate()))
				sed.setCreate(ped.getCreate());
			if (Strings.isBlank(sed.getDestroy()))
				sed.setDestroy(ped.getDestroy());
			if (Strings.isBlank(sed.getObtain()))
				sed.setObtain(ped.getObtain());
		}

		// 合并scope
		if (Strings.isBlank(self.getScope()))
			self.setScope(parent.getScope());

		// 合并构造方法参数
		if (!self.hasArgs())
			self.addArgs(parent.getArgs());

		// 合并字段
		for (FieldDesc fd : parent.getFields())
			if (!self.hasField(fd.getName()))
				self.addField(fd);

		return self;
	}
}
